//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-02-01
//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using JetBrains.Annotations;
using LargoCommon.Abstract;
namespace LargoCommon.Music
{
///
/// Musical Strip.
///
public class MusicalStrip
{
#region Fields
///
/// Musical Lines.
///
private IList lines;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The given context.
public MusicalStrip(MusicalContext givenContext)
: this() {
this.Context = givenContext;
}
///
/// Initializes a new instance of the class.
///
public MusicalStrip() {
this.lines = new List();
}
///
/// Initializes a new instance of the class.
///
/// The mark strip.
/// The given context.
public MusicalStrip(XElement markStrip, MusicalContext givenContext)
: this() {
Contract.Requires(markStrip != null);
if (markStrip == null) {
return;
}
this.Context = givenContext;
XElement xlines = markStrip.Element("Lines");
if (xlines == null) {
return;
}
var header = givenContext.Header;
var xelements = xlines.Elements();
foreach (XElement xline in xelements) {
MusicalLine line = new MusicalLine(xline, header) { Strip = this };
this.AddLine(line, false);
}
}
#endregion
#region Properties - Xml
/// Gets Xml representation.
/// Property description.
public virtual XElement GetXElement {
get {
var xstrip = new XElement(
"Strip",
new XAttribute("HasLines", this.HasAnyMusicalLine));
//// Lines
var xlines = new XElement("Lines");
//// Musical Lines to XML
foreach (MusicalLine line in this.Lines.Where(line => line != null)) {
//// line.MusicalBlock = givenMusicalBlock;
var xline = line.GetXElement;
xlines.Add(xline);
}
xstrip.Add(xlines);
return xstrip;
}
}
#endregion
#region Properties
///
/// Gets or sets the context.
///
///
/// The context.
///
public MusicalContext Context { get; set; }
///
/// Gets Musical Lines.
///
/// Property description.
public IList Lines {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.lines == null) {
throw new InvalidOperationException("Musical lines are null.");
}
return this.lines;
}
//// Remove private set - DevExpress
private set => this.lines = value;
}
/* Temporary - old code start .... */
///
/// Gets or sets NumberOfBars.
///
/// General musical property.
public byte NumberOfMelodicLines { get; set; }
///
/// Gets or sets NumberOfBars.
///
/// General musical property.
public byte NumberOfRhythmicLines { get; set; }
/* Temporary - old code end .... */
///
/// Gets a value indicating whether HasMusicalLine.
///
/// General musical property.
public bool HasAnyMusicalLine {
get {
var exists = (from mt in this.Lines where !mt.IsEmpty select 1).Any(); //// mt.IsSelected &&
return exists;
}
}
#endregion
#region Public methods
///
/// Gets lines having purpose.
///
/// The purpose.
///
/// Returns value.
///
public IList LinesHavingPurpose(LinePurpose purpose) {
var ls = (from line in this.Lines where line.Purpose == purpose select line).ToList();
return ls;
}
///
/// Sets the lines.
///
/// The given lines.
public void SetLines(IList givenLines) {
this.lines = givenLines;
}
///
/// Writes the body.
///
/// The given body.
public void WriteBody(MusicalBody givenBody) {
this.Context = givenBody.Context;
foreach (var line in this.lines) {
line.Strip = this; //// 2019/11 !?!
line.Tones.Clear();
}
foreach (var bar in givenBody.Bars) {
foreach (var element in bar.Elements) {
//// if (bar.BarNumber == 1 && element.Status != null) {
//// element.Status.Channel = element.Line.MainVoice.Channel; //// ?!? }
foreach (var tone in element.Tones) {
element.MusicalLine.Tones.Add(tone);
}
}
}
}
///
/// Deletes the line.
///
/// The line number.
public void DeleteLine(int lineIndex) {
if (lineIndex >= this.Lines.Count) {
return;
}
var line = this.Lines[lineIndex];
this.Lines.Remove(line);
this.RenumberLines();
}
///
/// Deletes the line.
///
/// The line identifier.
public void DeleteLine(Guid lineIdent) {
var line = this.GetLine(lineIdent);
if (line != null) {
this.Lines.Remove(line);
this.RenumberLines();
}
}
/// String with line details.
/// Returns value.
[UsedImplicitly]
public string LinesToString() {
var s = new StringBuilder();
s.Append(" Line Tones \n");
s.Append("-------------------------------------------------------------------\n");
var musicLines = from p in this.Lines orderby p.LineIndex descending select p;
foreach (var mline in
musicLines.Where(mline => mline?.Tones != null).Where(mline => !mline.IsEmpty)) {
s.Append(mline + "\r\n\n");
}
return s.ToString();
}
///
/// Finds the free channel.
///
/// The line number.
///
/// Returns value.
///
public MidiChannel FindFreeChannel(int lineIndex) {
//// int barNumber
//// Exclude not used lines like LinePurpose.Mute (?!)
var channels = (from line in this.Lines
where line.LineIndex != lineIndex
select line.MainVoice.Channel).Distinct().ToList();
for (byte channel = 0; channel <= MusicalProperties.MidiCountChannels; channel++) {
var midiChannel = (MidiChannel)channel;
if (midiChannel != MidiChannel.DrumChannel && !channels.Contains(midiChannel)) {
return midiChannel;
}
}
var c = (MidiChannel)(lineIndex % 16);
if (c != MidiChannel.DrumChannel) {
return c;
}
return MidiChannel.C00;
}
///
/// Clones the specified include tones.
///
/// if set to true [include tones].
/// Returns value.
public MusicalStrip Clone(bool includeTones) {
var strip = (MusicalStrip)this.Clone();
strip.Lines = new List();
foreach (var newLine in this.Lines.Select(line => line.Clone(includeTones))) {
//// newLine.IsComposed = false;
var ts = (List)strip.Lines;
ts.Add(newLine);
}
return strip;
}
///
/// Remove the lines.
///
public void ResetLines() {
this.lines = new List();
}
///
/// Resets the tones.
///
public void ResetTones() {
foreach (var line in this.lines) {
line.SetTones(new MusicalStrikeCollection());
}
}
///
/// Gets the musical line.
///
/// The line number.
/// Returns value.
[UsedImplicitly]
public MusicalLine GetLine(int lineIndex) {
//// bool exists = (from mt in this.MusicalLines where mt.IsSelected && !mt.IsEmpty select 1).Any();
if (lineIndex < 0 || lineIndex >= this.Lines.Count) {
return null;
}
var ts = (List)this.Lines;
return ts[lineIndex];
}
///
/// Gets the line.
///
/// The line identifier.
/// Returns value.
[UsedImplicitly]
public MusicalLine GetLine(Guid lineIdent) {
var ts = (List)this.Lines;
var line = (from t in ts where t.LineIdent == lineIdent select t).FirstOrDefault();
return line;
}
///
/// Add Musical Line.
///
/// Musical Line.
/// if set to true [renumber].
public void AddLine(MusicalLine line, bool renumber) {
if (line == null) {
return;
}
if (renumber) {
//// if (line.LineIndex == 0) { 2016/07
var num = this.Lines.Count;
line.LineIndex = (byte)num;
//// }
//// 2016/07 //// 2015/01
//// if (line.LineIndex == 0) { line.LineIndex = line.LineIndex; }
}
var ts = (List)this.Lines;
ts.Add(line);
this.Context.Header.NumberOfLines = (byte)ts.Count;
}
///
/// Removes the line.
///
/// The line.
[UsedImplicitly]
public void RemoveLine(MusicalLine line) {
if (line == null) {
return;
}
var ts = (List)this.Lines;
ts.Remove(line);
}
///
/// Moves the tones from edges.
///
/// The min note.
/// The max note.
public void MoveTonesFromEdges(byte minNote, byte maxNote) {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var mt in this.Lines.SelectMany(line => line.Tones.OfType().Where(mt => !mt.IsPause))) {
if (mt.IsTrueTone) {
mt.Pitch.MoveFromEdges(minNote, maxNote);
}
}
}
///
/// Corrects the octaves.
///
public void CorrectOctaves() {
foreach (var line in this.Lines) {
OrchestraChecker.Singleton.CorrectOctavesOfInstrumentedTones(line.Tones);
}
var setup = MusicalSettings.Singleton;
this.MoveTonesFromEdges(setup.NoteLowest, setup.NoteHighest);
}
///
/// Rebuild Channels.
///
public void RebuildChannels() {
if (this.Lines.Count <= 15) {
foreach (var line in this.Lines) {
line.MainVoice.Channel = line.FirstStatus.IsMelodic ? MusicalProperties.ChannelForPartNumber(line.LineIndex) : MidiChannel.DrumChannel;
}
return;
}
if (this.Lines.Count <= 30) {
foreach (var line in this.Lines) {
line.MainVoice.Channel = line.FirstStatus.IsMelodic ? MusicalProperties.ChannelForPartNumber(line.LineIndex / 2) : MidiChannel.DrumChannel;
}
}
}
///
/// Splits the same tones.
///
[UsedImplicitly]
public void SplitTheFollowUpTones() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
foreach (var line in this.Lines) {
SplitTheFollowUpTonesInLine(line);
}
}
#endregion
#region Public methods - Transformation
///
/// Total Horizontal Inversion.
///
[UsedImplicitly]
public void HorizontalInversion() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
this.Lines.ForAll(musicalLine => musicalLine.TotalHorizontalInversion());
}
///
/// Total Vertical Inversion.
///
[UsedImplicitly]
public void VerticalInversion() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var mt in this.Lines.SelectMany(line => line.Tones.OfType().Where(mt => !mt.IsPause))) {
mt.Pitch?.SetAltitude(128 - mt.Pitch.SystemAltitude);
}
}
///
/// Bar Horizontal Inversion.
///
[UsedImplicitly]
public void BarInversion() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
this.Lines.ForAll(musicalLine => musicalLine.BarHorizontalInversion());
}
///
/// Modular Deformation.
///
[UsedImplicitly]
public void ModularDeformation() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
const int deformation = 5;
var absDeformation = Math.Abs(deformation);
if (absDeformation <= 0) {
return;
}
foreach (var line in this.Lines) {
foreach (var tone in line.Tones) {
MusicalTone mt = tone as MusicalTone;
if (mt == null || mt.IsPause) {
continue;
}
var altitude = mt.Pitch.SystemAltitude;
var steps = mt.Pitch.SystemAltitude % absDeformation; //// MusMath.RandomNatural(3);
altitude = altitude + (Math.Sign(deformation) * steps);
mt.Pitch.SetAltitude(altitude);
}
}
}
///
/// Vertical Extension.
///
[UsedImplicitly]
public void VerticalExtension() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
foreach (var line in this.Lines) {
foreach (var tone in line.Tones) {
//// PlannedTones
MusicalTone mt = tone as MusicalTone;
if (mt == null || mt.IsPause || mt.IsEmpty) {
continue;
}
var altitude = mt.Pitch.SystemAltitude;
altitude = Math.Max((2 * altitude) - DefaultValue.MeanNoteAltitude, 0);
altitude = Math.Min(altitude, 127);
mt.Pitch.SetAltitude(altitude);
}
}
}
///
/// Vertical Narrowing.
///
[UsedImplicitly]
public void VerticalNarrowing() {
const byte nivelizationFactor = 2;
const byte altitudeShift = 48;
if (this.Lines == null || !this.Lines.Any()) {
return;
}
foreach (var line in this.Lines) {
foreach (var tone in line.Tones) {
//// PlannedTones
MusicalTone mt = tone as MusicalTone;
if (mt == null || mt.IsPause || mt.IsEmpty) {
continue;
}
var altitude = mt.Pitch.SystemAltitude;
altitude = Math.Max((altitude / nivelizationFactor) + altitudeShift, 0);
altitude = Math.Min(altitude, 127);
mt.Pitch.SetAltitude(altitude);
}
}
}
///
/// Shifts Octave Up.
///
[UsedImplicitly]
public void OctaveUp() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
foreach (var line in this.Lines) {
foreach (var mt in
line.Tones.OfType().Where(mt => !mt.IsPause && !mt.IsEmpty)) {
mt.Pitch.ShiftOctave(+1);
}
}
}
///
/// Shifts Octave Down.
///
[UsedImplicitly]
public void OctaveDown() {
if (this.Lines == null || !this.Lines.Any()) {
return;
}
foreach (var line in this.Lines) {
foreach (var mt in
line.Tones.OfType().Where(mt => !mt.IsPause && !mt.IsEmpty)) {
mt.Pitch.ShiftOctave(-1);
}
}
}
#endregion
#region String representation
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
s.Append(string.Format(CultureInfo.CurrentCulture, "Lines {0}", this.Lines.Count));
return s.ToString();
}
#endregion
#region Tones
/// List of tones in one bar of musical part.
/// Number of musical bar.
/// Returns value.
[UsedImplicitly]
public MusicalToneCollection MelodicTonesInBar(int barNumber) {
var tonesInBar = new List();
foreach (var mtones in from line in this.Lines
where line?.Tones != null //// && mline.IsSelected
where line.FirstStatus.LineType == MusicalLineType.Melodic
select line.MelodicTonesInBar(barNumber)
into mtones
where mtones != null && mtones.Any()
select mtones) {
tonesInBar.AddRange(mtones);
}
return new MusicalToneCollection(tonesInBar);
}
#endregion
#region Private static methods
///
/// Splits the follow up tones in line.
///
/// The line.
private static void SplitTheFollowUpTonesInLine(MusicalLine line) {
Contract.Requires(line != null);
MusicalTone lastLine = null;
foreach (var mt in
line.Tones.OfType().Where(mt => mt.IsTrueTone && !mt.IsPause)) {
if (lastLine != null && lastLine.IsGoingToNextBar
&& lastLine.BarNumber ==
mt.BarNumber + 1 //// && lastLine.BarNumberTo == mt.BarNumber
&& mt.IsFromPreviousBar
&& lastLine.Pitch.SystemAltitude == mt.Pitch.SystemAltitude
&& lastLine.BitTo == mt.BitFrom - 1) {
lastLine.Duration += mt.Duration;
//// lastLine.BarNumberTo = mt.BarNumberTo;
mt.Loudness = 0;
}
lastLine = mt;
}
}
#endregion
#region Private methods
/// Makes a deep copy of the MusicalStrip object.
/// Returns object.
private object Clone() {
var strip = new MusicalStrip(this.Context);
return strip;
}
///
/// Renumbers the lines.
///
private void RenumberLines() {
int lineIndex = 0;
foreach (var line in this.Lines) {
line.LineIndex = lineIndex++;
}
}
#endregion
}
}